#include "stdafx.h"
#include "..\headers\GameObject.h"
#include "..\headers\PhysicsWorld.h"
#include "..\headers\Components.h"
#include "..\headers\CameraComponents.h"
#include "..\headers\InputSystem.h"
#include "..\headers\ComponentMessenger.h"

namespace ne{

unsigned int ne::GameObject::s_counter = 0;
Ogre::SceneManager* GameObject::s_sceneManager = nullptr;
PhysicsWorld* GameObject::s_physicsWorld = nullptr;
InputSystem* GameObject::s_inputSystem = nullptr;

GameObject::GameObject(void){
	m_messenger = new ComponentMessenger;
	m_sceneNode = s_sceneManager->getRootSceneNode()->createChildSceneNode();
	m_sceneNode->setPosition(0,0,0);
	m_id = "GameObject" + Convert::NumberToString(s_counter);
	s_counter++;
	System::Msg("Created game object: "+m_id);
}

GameObject::~GameObject(void){Shut();if(m_messenger){delete m_messenger;m_messenger=nullptr;}}

luabind::object GameObject::GetAsLua(lua_State* L){return luabind::object(L,this);}

void GameObject::RemoveComponent(int type){

}

void GameObject::RemoveComponent(Component* component){

}

void GameObject::_RemoveComponent(Component* component){
	// If it is a rigid body we need to check for constraints and remove them as well
	if ((component->GetType() & COMPONENT_RIGIDBODY) == COMPONENT_RIGIDBODY){
		Component* constraintComponent = this->GetComponent(COMPONENT_CONSTRAINT);
		while (constraintComponent != nullptr){
			m_components.remove(constraintComponent);
			constraintComponent->Shut();
			delete constraintComponent;
			constraintComponent = this->GetComponent(COMPONENT_CONSTRAINT);
		}
	}
	m_components.remove(component);
	component->Shut();
	delete component;
	component = nullptr;
	// if we don't have any more update components remove this game object form the update list
}

Component* GameObject::GetComponent(int type){
	if (!m_components.empty()){
		for (auto it = m_components.begin(); it != m_components.end(); it++){
			if (((*it)->GetType() & type) == type){
				return (*it);
			}
		}
	}
	return nullptr;
}

void GameObject::Notify(CEvent* msg){m_messenger->Notify(msg);}
void GameObject::Register(IComponentObserver* ob){m_messenger->Register(ob);}
void GameObject::Unregister(IComponentObserver* ob){m_messenger->Unregister(ob);}

void GameObject::Shut(){
	// TODO: Do some work for parent/child relation when implemented
	if(!m_components.empty()){
		for (auto it = m_components.begin(); it != m_components.end(); it++){
			(*it)->Shut();
			delete *it;
			*it = nullptr;
		}
		m_components.clear();
	}
	if(m_sceneNode){
		s_sceneManager->destroySceneNode(m_sceneNode);
		m_sceneNode=nullptr;
	}
}

Component* GameObject::AddComponent(int type){
	Component* component = nullptr;
	switch (type){
	case COMPONENT_ANIMATION:
		component=new AnimationComponent;
		component->SetOwner(this);
		break;
	case COMPONENT_MESH:
		component=new MeshComponent;
		component->SetOwner(this);
		break;
	case COMPONENT_RIGIDBODY:
		component=new RigidBodyComponent;
		component->SetOwner(this);
		break;
	case COMPONENT_COLLIDER_BOX:
		component=new BoxColliderComponent;
		component->SetOwner(this);
		break;
	case COMPONENT_COLLIDER_SPHERE:
		component=new SphereColliderComponent;
		component->SetOwner(this);
		break;
	case COMPONENT_COLLIDER_STATIC_MESH:
		component=new StaticMeshColliderComponent;
		break;
	case COMPONENT_COLLIDER_CAPSULE:
		component=new CapsuleColliderComponent;
		break;
	case COMPONENT_LIGHT:
		component=new LightComponent;
		break;
	case COMPONENT_CAMERA:
		component=new CameraComponent;
		break;
	case COMPONENT_FIRST_PERSON_CAMERA:
		component=new FirstPersonCameraComponent;
		break;
	case COMPONENT_CHARACTER_CONTROLLER:
		component=new CharacterControllerComponent;
		break;
	case COMPONENT_THIRD_PERSON_CAMERA:
		component=new ThirdPersonCameraComponent;
		break;
	default:
		break;
	}
	if (component!=nullptr){component->SetOwner(this);component->Init();m_components.push_back(component);}
	else {System::Log("Failed to create component for game object"+m_id+". The specified type was not found");}
	return component;
}

luabind::object GameObject::AddComponent(int type, lua_State* L){
	Component* component = this->AddComponent(type);
	return component->GetAsLua(L);
}

luabind::object GameObject::GetComponent(int type, lua_State* L){
	Component* component = this->GetComponent(type);
	return component->GetAsLua(L);
}

// TODO: These member methods need to signal the rigidbody (if any) to set a new position when used
void GameObject::SetPosition(float x, float y, float z){
	m_sceneNode->setPosition(x,y,z);
	CEventVec3 evt(NE_MSG_SET_POSITION,Ogre::Vector3(x,y,z));
	m_messenger->Notify(&evt);
}

void GameObject::SetPosition(const Ogre::Vector3& vec3){
	m_sceneNode->setPosition(vec3);
	CEventVec3 evt(NE_MSG_SET_POSITION,vec3);
	m_messenger->Notify(&evt);
}

void GameObject::Translate(float x, float y, float z){
	m_sceneNode->translate(x,y,z);
	CEventVec3 evt(NE_MSG_SET_POSITION,m_sceneNode->getPosition());
	m_messenger->Notify(&evt);
}

void GameObject::Translate(const Ogre::Vector3& vec3){
	m_sceneNode->translate(vec3);
	CEventVec3 evt(NE_MSG_SET_POSITION,m_sceneNode->getPosition());
	m_messenger->Notify(&evt);
}

void GameObject::SetScale(float x,float y, float z){m_sceneNode->setScale(x,y,z);}
void GameObject::SetScale(const Ogre::Vector3& scale){m_sceneNode->setScale(scale);}

}